/********************************************************
 *
 * FILE NAME:   plotsignal.cpp
 *
 * DESCRIPTION:
 *  Display input signal on screen
 *
 ********************************************************/

/********************************************************/
/*              Includes                                */
/********************************************************/

#include "plot.h"
#include <QStringBuilder>

/********************************************************/
/*              Defines                                 */
/********************************************************/

/********************************************************/
/*              Macros                                  */
/********************************************************/

/********************************************************/
/*              Typedefs                                */
/********************************************************/

/********************************************************/
/*              Local Functions Declarations (LOCAL)    */
/********************************************************/

/********************************************************/
/*              Global Variables (GLOBAL/IMPORT)        */
/********************************************************/

/********************************************************/
/*              Local Module Variables (MODULE)         */
/********************************************************/

/********************************************************/
/*              Local File Variables (LOCAL)            */
/********************************************************/
size_t Plot::_numOfActive = 0;
QMutex Plot::_numOfActiveMtx;

/********************************************************/
/*              Functions Definitions (LOCAL/GLOBAL)    */
/********************************************************/
Plot::Plot(QQuickItem *parent): QQuickPaintedItem(parent)
{
    _sampleNum = 0;
    _signalDuration = 5;
    _sampleFreq = 50;

    _customPlot = new QCustomPlot();
    _workerThread = new QThread(this);

    _plotMutex = new QMutex;
    _plotPending = false;

    _worker = new PlotWorker(this);
    _worker->moveToThread(_workerThread);
    connect(_workerThread, SIGNAL(started()), _worker, SLOT(start()));
    connect(_worker, SIGNAL(replot()), this, SLOT(update()));

    setFlag( QQuickItem::ItemHasContents, true );
    setRenderTarget(QQuickPaintedItem::FramebufferObject);
    setPerformanceHint(QQuickPaintedItem::FastFBOResizing);
    setAntialiasing(false);
    setSmooth(false);
    connect(this, SIGNAL(widthChanged()), this, SLOT(updateQCustomPlotSize()));
    connect(this, SIGNAL(heightChanged()), this, SLOT(updateQCustomPlotSize()));
}

Plot::~Plot()
{
    delete _customPlot;
    delete _workerThread;
    delete _worker;
}

void Plot::init()
{
    _burstLength = 5;
    _iSignal = new ISignal(0, _burstLength);

    _customPlot->addGraph();
    _customPlot->graph(0)->setPen(QPen(QColor(0, 255, 0), 1));
    _customPlot->addGraph();
    _customPlot->graph(1)->setPen(QPen(QColor(0, 255, 0), 1));

    _customPlot->xAxis->setTickLabels(false);
    _customPlot->yAxis->setTickLabels(false);
    _customPlot->xAxis->setBasePen(QPen(Qt::white, 1));
    _customPlot->yAxis->setBasePen(QPen(Qt::white, 1));
    _customPlot->xAxis->setTickPen(QPen(Qt::white, 1));
    _customPlot->yAxis->setTickPen(QPen(Qt::white, 1));
    _customPlot->xAxis2->setBasePen(QPen(Qt::white, 1));
    _customPlot->yAxis2->setBasePen(QPen(Qt::white, 1));
    _customPlot->xAxis2->setTickPen(QPen(Qt::white, 1));
    _customPlot->yAxis2->setTickPen(QPen(Qt::white, 1));
    _customPlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
    _customPlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
    _customPlot->xAxis->grid()->setZeroLinePen(Qt::NoPen);
    _customPlot->yAxis->grid()->setZeroLinePen(Qt::NoPen);
    _customPlot->setBackground(QBrush(QColor(0, 0, 0)));
    _customPlot->axisRect()->setBackground(QBrush(QColor(0, 0, 0)));
    _customPlot->yAxis->setAutoTickStep(false);
    _customPlot->yAxis->setAutoTickStep(false);
    _customPlot->yAxis->setLabelColor(QColor(0, 255, 0,180));
    _customPlot->yAxis->setLabelFont(_labelFont);

    _xTickNum = 10;
    _yTickNum = 10 * height()/width();

   _labelFont.setPixelSize(height()/9);
   _customPlot->xAxis->setLabelFont(_labelFont);
   _customPlot->xAxis->setLabelColor(QColor(0, 255, 0,180));

    _customPlot->xAxis->setAutoTickStep(false);
    _customPlot->xAxis->setTickStep(_signalDuration/_xTickNum);

    _customPlot->axisRect()->setupFullAxesBox(true);

    _windowWidth = _signalDuration * _sampleFreq;
    while((_windowWidth % _burstLength) != 0)
        _windowWidth--;

    _customPlot->xAxis->setRange(0,_signalDuration , Qt::AlignLeft);
    _plotPixmap = new QPixmap(width(), height());
}

void Plot::start()
{
    _workerThread->start();
}

void Plot::paint(QPainter *painter)
{
    painter->drawPixmap(0, 0, *_plotPixmap);

    _numOfActiveMtx.lock();
    if(_plotPending)
    {
        --_numOfActive;
    }
    _numOfActiveMtx.unlock();
    _plotPending = false;
}

void Plot::setSignalDuration(double signalDuration)
{
       if(_signalDuration == signalDuration)
           return;

       _signalDuration = signalDuration;
       _customPlot->xAxis->setRange(0,_signalDuration , Qt::AlignLeft);
       _customPlot->xAxis->setTickStep(_signalDuration/_xTickNum);
       _xLabelText = _name + QString::number(_signalDuration/_xTickNum, 'g', 2) + QString(" [s/Div]");
       _customPlot->xAxis->setLabel(_xLabelText);
       _customPlot->xAxis->setLabelFont(_labelFont);
       _windowWidth = _signalDuration * _sampleFreq;
       while((_windowWidth % _burstLength) != 0)
           _windowWidth--;

       _sampleNum = 0;
       _customPlot->graph(0)->clearData();
       _customPlot->graph(1)->clearData();

       emit signalDurationChanged();
       update();
}

void Plot::setSignalActivity(bool signalActivity)
{
    _signalActive = signalActivity;
    emit signalActivityChanged();
}

void Plot::setName(QString name)
{
    _name = name;
    emit nameChanged();
    _xLabelText = _name + QString::number(_signalDuration/_xTickNum, 'g', 2) + QString(" [s/Div]");

    if(_xLabelText.indexOf("Sync") != -1 )
    {
        _units = "";
    }
    else
    {
        if((int)_xLabelText.indexOf("Gyroscope") != -1)
        {
            _units = "";
        }
        else
        {
            _units = QString(" [μV/Div]");
        }
    }
   _customPlot->xAxis->setLabel(_xLabelText);
}

void Plot::updateQCustomPlotSize()
{
    _plotMutex->lock();
    _customPlot->setGeometry(0, 0, width(), height());
    _yTickNum = 10 * height()/width();
    _labelFont.setPixelSize(height()/10);
    _customPlot->xAxis->setLabelFont(_labelFont);


     _customPlot->yAxis->rescale(true);

    *_plotPixmap = _customPlot->toPixmap(_customPlot->width(), _customPlot->height());
    _plotMutex->unlock();
    update();
}

PlotWorker::PlotWorker(Plot *context): QObject( 0 ),_context(context),_sampleNum(0){}

PlotWorker::~PlotWorker()
{
}

void PlotWorker::start()
{
    bool &_plotPending = _context->_plotPending;
    bool &_signalActive = _context->_signalActive;
    size_t &_yTickiNum = _context->_yTickNum;
    size_t &_windowWidth = _context->_windowWidth;
    size_t &_sampleNum = _context->_sampleNum;
    QPixmap &_plotPixmap = *(_context->_plotPixmap);
    QFont &_labelFont = _context->_labelFont;

    size_t _burstLength = _context->_burstLength;
    double _signalDuration = _context->_signalDuration;
    QCustomPlot *_customPlot = _context->_customPlot;
    ISignal *_iSignal = _context->_iSignal;
    QMutex *_plotMutex = _context->_plotMutex;

    QVector<double> _values(_burstLength);
    QVector<double> _keys(_burstLength);
    QString _yLabelText;
    QString &_units = _context->_units;
    double &_sampleFreq = _context->_sampleFreq;

    size_t &numOfActive = _context->_numOfActive;
    QMutex &numOfActiveMtx = _context->_numOfActiveMtx;

    int maUpdateInterval = 50;
    int maLength = 200;
    float rangeMagnification = 2;
    float lowerThreshold = 0.1;
    float upperThreshold = 0.8;

    int maCnt = 0;
    Buffer<sample_t> maBuffer(maLength);
    sample_t maAvg = 0;
    sample_t maMax = 10;

    sample_t rangeAvg = 0;
    sample_t rangeMax = rangeMagnification * maMax;

    QCPRange range;
    double tickStep;

    while(_iSignal->requestBurst(_burstLength) == true)
    {
        for (size_t i = 0;i < _burstLength;i++)
        {
            _keys[i] = ((_sampleNum+i)%_windowWidth)/_sampleFreq;
            _iSignal->readInSample();
            _values[i] = (*_iSignal)[0];

            maBuffer.rotate(1);
            maBuffer[0] = (*_iSignal)[0];

            if(++maCnt >= maUpdateInterval)
            {
                maCnt = 0;

                maAvg = 0;
                for(int j = 0; j < maLength; ++j)
                {
                    maAvg += maBuffer[j] / maLength;
                }

                maMax = 10;
                for(int j = 0; j < maLength; ++j)
                {
                    maMax = std::max(maMax, std::abs(maAvg - maBuffer[j]));
                }
            }
        }

        _plotMutex->lock();
        _customPlot->graph(0)->addData(_keys,_values);

        if (qAbs(maxValue(_keys) - (_windowWidth - 1)/_sampleFreq) < 0.0001)
        {
            _customPlot->graph(1)->clearData();
            _customPlot->graph(1)->addData(*(_customPlot->graph(0)->data()));
            _customPlot->graph(0)->clearData();
        }
        else
        {
            _customPlot->graph(1)->removeDataBefore(_keys[_burstLength-1]+_signalDuration/50);
        }
        _plotMutex->unlock();

        _sampleNum += _burstLength;
        _sampleNum %= _windowWidth;

        numOfActiveMtx.lock();
        if((_plotPending == false && _signalActive && _context->isVisible()) && numOfActive < 6)
        {
            ++numOfActive;
            numOfActiveMtx.unlock();

            _plotMutex->lock();

            if((maAvg - maMax) < (rangeAvg - rangeMax * upperThreshold) || \
                (maAvg - maMax) > (rangeAvg - rangeMax * lowerThreshold) || \
                (maAvg + maMax) > (rangeAvg + rangeMax * upperThreshold) || \
                (maAvg + maMax) < (rangeAvg + rangeMax * lowerThreshold))
            {
                rangeAvg = maAvg;
                rangeMax = maMax * rangeMagnification;
                _customPlot->yAxis->setRange(rangeAvg - rangeMax, rangeAvg + rangeMax);
            }

            range = _customPlot->yAxis->range();
            tickStep = (range.upper-range.lower)/_yTickiNum;

            _yLabelText =  QString::number(tickStep, 'g', 2) + _units;
            _customPlot->yAxis->setLabel(_yLabelText);
            _customPlot->yAxis->setTickStep(tickStep);
            _customPlot->yAxis2->setTickStep(tickStep);

            _plotPixmap = _customPlot->toPixmap(_customPlot->width(), _customPlot->height());
            _plotMutex->unlock();
            _plotPending = true;
            emit replot();
        }else
        {
            numOfActiveMtx.unlock();
        }
    }
}

double PlotWorker::maxValue(QVector<double> values)
{
    double max = values[0];
    for(int i = 1 ; i < values.size();i++)
    {
        if (values[i] > max)
            max = values[i];
    }
    return max;
}

